﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CliffPusher
{
    public partial class ucPlayerLocation : UserControl
    {
        Pen _borderPen;


        public ucPlayerLocation()
        {
            InitializeComponent();
            if( Players == null )
            {
                Players = new List<PlayerTokenStack>();
            }

            _borderPen = new Pen(Color.Black, 4);
            this.Disposed += UcPlayerLocation_Disposed;
        }

        private void UcPlayerLocation_Disposed(object sender, EventArgs e)
        {
            foreach( var draw in _dctDraw.Values )
            {
                draw.Dispose();
            }
        }

        // [Description("Collection of players"), Category("Data"), ]
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public List<PlayerTokenStack> Players { get; set; }

        [Description("How many horizontal tokens to draw before adding a new line"), Category("Data")]
        public int HorizontalLocations { get; set; } = 2;

        [Description("Number of spots available"), Category("Data")]
        public int MaxPlayers { get; set; } = 4;

        class PlayerDrawData : IDisposable
        {
            public PlayerDrawData(Color backColor, Color textColor)
            {
                Pen = new Pen(backColor);
                BackBrush = new SolidBrush(backColor);
                ForeBrush = new SolidBrush(textColor);
            }

            public Pen Pen { get; private set; }
            public Brush BackBrush { get; private set; }
            public Brush ForeBrush { get; private set; }

            public void Dispose()
            {
                Pen.Dispose();
                BackBrush.Dispose();
                ForeBrush.Dispose();
            }
        }

        [Serializable()]
        public class PlayerTokenStack
        {
            public PlayerTokenStack()
                : this(Color.Red)
            {

            }

            public PlayerTokenStack(Color color)
                : this(color, Color.Black)
            {

            }
            public PlayerTokenStack(Color color, Color textColor  )
            {
                BackColor = color;
                TextColor = textColor;
            }
            
            public Color BackColor { get; set; }
            public Color TextColor { get; set; }

            public int _count = 0;
            public int Count
            {
                get
                {
                    return _count;
                }
                set
                {
                    if( value != _count )
                    {
                        if(CountChanging != null)
                        {
                            if( !CountChanging(this, _count) ) // disallow change
                            {
                                return;
                            }
                        }
                        _count = value;
                    }
                }
            }

            public event Func<PlayerTokenStack, int, bool> CountChanging;
        }

        private Dictionary<PlayerTokenStack, PlayerDrawData> _dctDraw = new Dictionary<PlayerTokenStack, PlayerDrawData>();

        private class DrawData
        {
            public int VerticalLocations { get; set; }

            public int HorizontalLocations { get; set; }

            public Point BaseLocation { get; set; }

            public Size TokenSize { get; set; }

        }

        private DrawData GetDrawData(Rectangle mainRectangle)
        {
            var data = new DrawData
            {
                VerticalLocations = (int)Math.Round((double)MaxPlayers / (double)HorizontalLocations, MidpointRounding.AwayFromZero),
                HorizontalLocations = this.HorizontalLocations
            };

            System.Diagnostics.Debug.Assert((data.VerticalLocations * HorizontalLocations) >= MaxPlayers);

            int inflate = (int)(_borderPen.Width + 2) * -1; // 2 extra gap between edges
            var tokensRectangle = Rectangle.Inflate(mainRectangle, inflate, inflate);
            data.BaseLocation = tokensRectangle.Location;
            data.TokenSize = new Size((tokensRectangle.Width / HorizontalLocations), (tokensRectangle.Height / data.VerticalLocations));

            return data;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            var data = GetDrawData(e.ClipRectangle);

            e.Graphics.DrawRectangle(_borderPen, e.ClipRectangle);

            int playerIndex = 0;

            var activePlayers = Players.Where(p => p.Count > 0).ToArray();
            for(int verticalIndex = 0; verticalIndex < data.VerticalLocations; verticalIndex++)
            {
                for( int horizontalIndex = 0; horizontalIndex < data.HorizontalLocations; horizontalIndex++ )
                {
                    if( playerIndex >= activePlayers.Length )
                    {
                        return;
                    }
                    var player = activePlayers[playerIndex++];
                    if( !_dctDraw.ContainsKey(player) )
                    {
                        _dctDraw.Add(player, new PlayerDrawData(player.BackColor, player.TextColor));
                    }
                    var draw = _dctDraw[player];
                    
                    var tokenRectangle = new Rectangle(
                        data.BaseLocation.X + (data.TokenSize.Width * horizontalIndex),
                        data.BaseLocation.Y + (data.TokenSize.Height * verticalIndex),
                        data.TokenSize.Width - 4,
                        data.TokenSize.Height - 4);
                    e.Graphics.FillEllipse(draw.BackBrush, tokenRectangle);
                    var text = player.Count.ToString();
                    var fontSize = e.Graphics.MeasureString(text, _font);

                    e.Graphics.DrawString(text, _font, draw.ForeBrush,
                        tokenRectangle.Location.X + ((tokenRectangle.Width - fontSize.Width) / 2),
                        tokenRectangle.Location.Y + ((tokenRectangle.Height - fontSize.Height) / 2) );
                }
            }
        }

        private bool PlayerCountChanging( PlayerTokenStack player, int oldCount )
        {
            this.Invalidate();
            return true;
        }

        Font _font;
        void CalculateFont()
        {
            var data = GetDrawData(this.ClientRectangle);
            _font = new Font(FontFamily.GenericSansSerif, data.TokenSize.Height / 2, GraphicsUnit.Pixel);
        }

        
        private void ucPlayerLocation_Load(object sender, EventArgs e)
        {
            if (this.DesignMode && Players.Count == 0 )
            {
                Players.Add(new PlayerTokenStack(Color.Red) { Count = 4 });
                Players.Add(new PlayerTokenStack(Color.Green) { Count = 2 });
                Players.Add(new PlayerTokenStack(Color.Blue) { Count = 1 });
                Players.Add(new PlayerTokenStack(Color.Yellow) { Count = 2 });
            }
            Players.ForEach( player => player.CountChanging += PlayerCountChanging );
            CalculateFont();
        }

        private void ucPlayerLocation_SizeChanged(object sender, EventArgs e)
        {
            this.Invalidate();
            CalculateFont();
        }
    }
}
